%% ``The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved via the world wide web at http://www.erlang.org/.
%% 
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%% 
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
%% AB. All Rights Reserved.''
%% 
%%     $Id$
%%
-include_lib("ect/include/ect.hrl").

%% This class is the modified version of erl_id_trans.erl from the Erlang source code.
%% A state-variable is added to each function, to turn them into methods.
-class(idtrans).


%% A identity transformer of Erlang abstract syntax.

%% This module only traverses legal Erlang code. This is most noticeable
%% in guards where only a limited number of expressions are allowed.
%% N.B. if this module is to be used as a basis for tranforms then
%% all the error cases must be handled otherwise this module just crashes!

-methods([
	 parse_transform/3, 
	 forms/2, 
	 form/2, 
	 pattern/2, 
	 expr/2, 
	 gexpr/2, 
	 function/4, 
	 record_defs/2, 
	 icr_clauses/2,
	 head/2,
	 patterns/2,
	 clauses/2,
	 guard/2,
	 guard0/2,
	 guard_test/2,
	 bit_types/2,
	 pattern_list/2,
	 pattern_fields/2,
	 gexpr_list/2,
	 grecord_inits/2,
	 lc_bc_quals/2,
	 clause/2,
	 pattern_grp/2,
	 exprs/2,
	 expr_list/2,
	 record_inits/2,
	 record_updates/2,
	 fun_clauses/2,
	 farity_list/2
	]).

-export([parse_transform/2]). % for testing

parse_transform(Forms, _Options) ->
    This = #idtrans{},
    {_, F} = {This}:parse_transform(Forms, _Options),
    F.

parse_transform(This, Forms, _Options) ->
    {This}:forms(Forms).

%% forms(Fs) -> lists:map(fun (F) -> form(F) end, Fs).

forms(This1, [F0|Fs0]) ->
    {This2, F1} = {This1}:form(F0),
    {This3, Fs1} = {This2}:forms(Fs0),
    {This3, [F1|Fs1]};
forms(This, []) -> {This, []}.

%% -type form(Form) -> Form.
%%  Here we show every known form and valid internal structure. We do not
%%  that the ordering is correct!

%% First the various attributes.
form(This, {attribute,Line,module,Mod}) ->
    {This, {attribute,Line,module,Mod}};
form(This, {attribute,Line,file,{File,Line}}) ->	%This is valid anywhere.
    {This, {attribute,Line,file,{File,Line}}};
form(This1, {attribute,Line,export,Es0}) ->
    {This2, Es1} = {This1}:farity_list(Es0),
    {This2, {attribute,Line,export,Es1}};
form(This1, {attribute,Line,import,{Mod,Is0}}) ->
    {This2, Is1} = {This1}:farity_list(Is0),
    {This2, {attribute,Line,import,{Mod,Is1}}};
form(This, {attribute,Line,compile,C}) ->
    {This, {attribute,Line,compile,C}};
form(This1, {attribute,Line,record,{Name,Defs0}}) ->
    {This2, Defs1} = record_defs(This1, Defs0),
    {This2, {attribute,Line,record,{Name,Defs1}}};
form(This, {attribute,Line,asm,{function,N,A,Code}}) ->
    {This, {attribute,Line,asm,{function,N,A,Code}}};
form(This, {attribute,Line,Attr,Val}) ->		%The general attribute.
    {This, {attribute,Line,Attr,Val}};
form(This1, {function,Line,Name0,Arity0,Clauses0}) ->
    {This2, {Name,Arity,Clauses}} = {This1}:function(Name0, Arity0, Clauses0),
    {This2, {function,Line,Name,Arity,Clauses}};
% Mnemosyne, ignore...
form(This, {rule,Line,Name,Arity,Body}) ->
    {This, {rule,Line,Name,Arity,Body}}; % Dont dig into this
%% Extra forms from the parser.
form(This, {error,E}) -> {This, {error,E}};
form(This, {warning,W}) -> {This,{warning,W}};
form(This, {eof,Line}) -> {This, {eof,Line}}.

%% -type farity_list([Farity]) -> [Farity] when Farity <= {atom(),integer()}.

farity_list(This1, [{Name,Arity}|Fas]) ->
    {This2, Fas2} = {This1}:farity_list(Fas),
    {This2, [{Name,Arity}|Fas2]};
farity_list(This, []) -> {This, []}.

%% -type record_defs([RecDef]) -> [RecDef].
%%  N.B. Field names are full expressions here but only atoms are allowed
%%  by the *parser*!

record_defs(This1, [{record_field,Line,{atom,La,A},Val0}|Is]) ->
    {This2, Val1} = {This1}:expr(Val0),
    {This3, Is1} = {This2}:record_defs(Is),
    {This3,
     [{record_field,Line,{atom,La,A},Val1}|Is1]
    };
record_defs(This1, [{record_field,Line,{atom,La,A}}|Is]) ->
    {This2, Is1} = {This1}:record_defs(Is),
    {This2, [{record_field,Line,{atom,La,A}}|Is1]};
record_defs(This, []) -> {This, []}.

%% -type function(atom(), integer(), [Clause]) -> {atom(),integer(),[Clause]}.

function(This1, Name, Arity, Clauses0) ->
    {This2, Clauses1} = {This1}:clauses(Clauses0),
    {This2, {Name,Arity,Clauses1}}.

%% -type clauses([Clause]) -> [Clause].

clauses(This1, [C0|Cs]) ->
    {This2, C1} = {This1}:clause(C0),
    {This3, Cs1} = {This2}:clauses(Cs),
    {This3, [C1|Cs1]};
clauses(This, []) -> {This, []}.

%% -type clause(Clause) -> Clause.

clause(This1, {clause,Line,H0,G0,B0}) ->
    {This2, H1} = {This1}:head(H0),
    {This3, G1} = {This2}:guard(G0),
    {This4, B1} = {This3}:exprs(B0),
    {This4, {clause,Line,H1,G1,B1}}.

%% -type head([Pattern]) -> [Pattern].

head(This, Ps) -> {This}:patterns(Ps).

%% -type patterns([Pattern]) -> [Pattern].
%%  These patterns are processed "sequentially" for purposes of variable
%%  definition etc.

patterns(This1, [P0|Ps]) ->
    {This2, P1} = {This1}:pattern(P0),
    {This3, Ps1} = {This2}:patterns(Ps),
    {This3, [P1|Ps1]};
patterns(This, []) -> {This, []}.

%% -type pattern(Pattern) -> Pattern.
%%  N.B. Only valid patterns are included here.

pattern(This, {var,Line,V}) -> {This, {var,Line,V}};
pattern(This1, {match,Line,L0,R0}) ->
    {This2, L1} = {This1}:pattern(L0),
    {This3, R1} = {This2}:pattern(R0),
    {This3, {match,Line,L1,R1}};
pattern(This, {integer,Line,I}) -> {This, {integer,Line,I}};
pattern(This, {char,Line,C}) -> {This, {char,Line,C}};
pattern(This, {float,Line,F}) -> {This, {float,Line,F}};
pattern(This, {atom,Line,A}) -> {This, {atom,Line,A}};
pattern(This, {string,Line,S}) -> {This, {string,Line,S}};
pattern(This, {nil,Line}) -> {This, {nil,Line}};
pattern(This1, {cons,Line,H0,T0}) ->
    {This2, H1} = {This1}:pattern(H0),
    {This3, T1} = {This2}:pattern(T0),
    {This3, {cons,Line,H1,T1}};
pattern(This1, {tuple,Line,Ps0}) ->
    {This2, Ps1} = {This1}:pattern_list(Ps0),
    {This2, {tuple,Line,Ps1}};
%%pattern({struct,Line,Tag,Ps0}) ->
%%    Ps1 = pattern_list(Ps0),
%%    {struct,Line,Tag,Ps1};
pattern(This1, {record,Line,Name,Pfs0}) ->
    {This2, Pfs1} = {This1}:pattern_fields(Pfs0),
    {This2, {record,Line,Name,Pfs1}};
pattern(This1, {record_index,Line,Name,Field0}) ->
    {This2, Field1} = {This1}:pattern(Field0),
    {This2, {record_index,Line,Name,Field1}};
%% record_field occurs in query expressions
pattern(This1, {record_field,Line,Rec0,Name,Field0}) ->
    {This2, Rec1} = {This1}:expr(Rec0),
    {This3, Field1} = {This2}:expr(Field0),
    {This3, {record_field,Line,Rec1,Name,Field1}};
pattern(This1, {record_field,Line,Rec0,Field0}) ->
    {This2, Rec1} = {This1}:expr(Rec0),
    {This3, Field1} = {This2}:expr(Field0),
    {This3, {record_field,Line,Rec1,Field1}};
pattern(This1, {bin,Line,Fs}) ->
    {This2, Fs2} = {This1}:pattern_grp(Fs),
    {This2, {bin,Line,Fs2}};
pattern(This, {op,Line,Op,A}) ->
    {This, {op,Line,Op,A}};
pattern(This, {op,Line,Op,L,R}) ->
    {This, {op,Line,Op,L,R}}.

pattern_grp(This1, [{bin_element,L1,E1,S1,T1} | Fs]) ->
    {This2, S2} = case S1 of
		      default ->
			  {This1, default};
		      _ ->
			  {This1}:expr(S1)
		  end,
    {This3, T2} = case T1 of
		      default ->
			  {This2, default};
		      _ ->
			  {This2}:bit_types(T1)
		  end,
    {This4, E11} = {This3}:expr(E1),
    {This5, Fs1} = {This4}:pattern_grp(Fs),
    {This5, [{bin_element,L1,E11,S2,T2} | Fs1]};
pattern_grp(This, []) ->
    {This, []}.

bit_types(This, []) ->
    {This, []};
bit_types(This1, [Atom | Rest]) when atom(Atom) ->
    {This2, Rest1} = {This1}:bit_types(Rest),
    {This2, [Atom | Rest1]};
bit_types(This1, [{Atom, Integer} | Rest]) when atom(Atom), integer(Integer) ->
    {This2, Rest1} = {This1}:bit_types(Rest),
    {This2, [{Atom, Integer} | Rest1]}.



%% -type pattern_list([Pattern]) -> [Pattern].
%%  These patterns are processed "in parallel" for purposes of variable
%%  definition etc.

pattern_list(This1, [P0|Ps]) ->
    {This2, P1} = {This1}:pattern(P0),
    {This3, Ps1} = {This2}:pattern_list(Ps),
    {This3, [P1|Ps1]};
pattern_list(This, []) -> {This, []}.

%% -type pattern_fields([Field]) -> [Field].
%%  N.B. Field names are full expressions here but only atoms are allowed
%%  by the *linter*!.

pattern_fields(This1, [{record_field,Lf,{atom,La,F},P0}|Pfs]) ->
    {This2, P1} = {This1}:pattern(P0),
    {This3, Pfs1} = {This2}:pattern_fields(Pfs),
    {This3, [{record_field,Lf,{atom,La,F},P1}|Pfs1]};
pattern_fields(This1, [{record_field,Lf,{var,La,'_'},P0}|Pfs]) ->
    {This2, P1} = {This1}:pattern(P0),
    {This3, Pfs1} = {This2}:pattern_fields(Pfs),
    {This3, [{record_field,Lf,{var,La,'_'},P1}|Pfs1]};
pattern_fields(This, []) -> {This, []}.

%% -type guard([GuardTest]) -> [GuardTest].

guard(This1, [G0|Gs]) when list(G0) ->
    {This2, G1} = {This1}:guard0(G0),
    {This3, Gs1} = {This2}:guard(Gs),
    {This3, [G1 | Gs1]};
guard(This, L) ->
    guard0(This, L).

guard0(This1, [G0|Gs]) ->
    {This2, G1} =  {This1}:guard_test(G0),
    {This3, Gs1} = {This2}:guard0(Gs),
    {This3, [G1|Gs1]};
guard0(This, []) -> {This, []}.

guard_test(This1, Expr={call,Line,{atom,La,F},As0}) ->
    case erl_internal:type_test(F, length(As0)) of
	true -> 
	    {This2, As1} = {This1}:gexpr_list(As0),
	    {This2, {call,Line,{atom,La,F},As1}};
	_ ->
	    {This1}:gexpr(Expr)
    end;
guard_test(This, Any) ->
    {This}:gexpr(Any).

%% Before R9, there were special rules regarding the expressions on
%% top level in guards. Those limitations are now lifted - therefore
%% there is no need for a special clause for the toplevel expressions.
%% -type gexpr(GuardExpr) -> GuardExpr.

gexpr(This, {var,Line,V}) -> {This, {var,Line,V}};
gexpr(This, {integer,Line,I}) -> {This, {integer,Line,I}};
gexpr(This, {char,Line,C}) -> {This, {char,Line,C}};
gexpr(This, {float,Line,F}) -> {This, {float,Line,F}};
gexpr(This, {atom,Line,A}) -> {This, {atom,Line,A}};
gexpr(This, {string,Line,S}) -> {This, {string,Line,S}};
gexpr(This, {nil,Line}) -> {This, {nil,Line}};
gexpr(This1, {cons,Line,H0,T0}) ->
    {This2, H1} = {This1}:gexpr(H0),
    {This3, T1} = {This2}:gexpr(T0),				%They see the same variables
    {This3, {cons,Line,H1,T1}};
gexpr(This1, {tuple,Line,Es0}) ->
    {This2, Es1} = {This1}:gexpr_list(Es0),
    {This2, {tuple,Line,Es1}};
gexpr(This1, {record_index,Line,Name,Field0}) ->
    {This2, Field1} = {This1}:gexpr(Field0),
    {This2, {record_index,Line,Name,Field1}};
gexpr(This1, {record_field,Line,Rec0,Name,Field0}) ->
    {This2, Rec1} = {This1}:gexpr(Rec0),
    {This3, Field1} = {This2}:gexpr(Field0),
    {This3, {record_field,Line,Rec1,Name,Field1}};
gexpr(This1, {record,Line,Name,Inits0}) ->
    {This2, Inits1} = {This1}:grecord_inits(Inits0),
    {This2, {record,Line,Name,Inits1}};
gexpr(This1, {call,Line,{atom,La,F},As0}) ->
    case erl_internal:guard_bif(F, length(As0)) of
	true -> {This2, As1} = {This1}:gexpr_list(As0),
		{This2, {call,Line,{atom,La,F},As1}}
    end;
% Guard bif's can be remote, but only in the module erlang...
gexpr(This1, {call,Line,{remote,La,{atom,Lb,erlang},{atom,Lc,F}},As0}) ->
    case erl_internal:guard_bif(F, length(As0)) or
	 erl_internal:arith_op(F, length(As0)) or 
	 erl_internal:comp_op(F, length(As0)) or
	 erl_internal:bool_op(F, length(As0)) of
	true -> {This2, As1} = {This1}:gexpr_list(As0),
		{This2, {call,Line,{remote,La,{atom,Lb,erlang},{atom,Lc,F}},As1}}
    end;
% Unfortunately, writing calls as {M,F}(...) is also allowed.
gexpr(This1, {call,Line,{tuple,La,[{atom,Lb,erlang},{atom,Lc,F}]},As0}) ->
    case erl_internal:guard_bif(F, length(As0)) or
	 erl_internal:arith_op(F, length(As0)) or 
	 erl_internal:comp_op(F, length(As0)) or
	 erl_internal:bool_op(F, length(As0)) of
	true -> {This2, As1} = {This1}:gexpr_list(As0),
		{This2, {call,Line,{tuple,La,[{atom,Lb,erlang},{atom,Lc,F}]},As1}}
    end;
gexpr(This1, {bin,Line,Fs}) ->
    {This2, Fs2} = {This1}:pattern_grp(Fs),
    {This2, {bin,Line,Fs2}};
gexpr(This1, {op,Line,Op,A0}) ->
    case erl_internal:arith_op(Op, 1) or 
	 erl_internal:bool_op(Op, 1) of
	true -> {This2, A1} = {This1}:gexpr(A0),
		{This2, {op,Line,Op,A1}}
    end;
gexpr(This1, {op,Line,Op,L0,R0}) when Op =:= 'andalso'; Op =:= 'orelse' ->
    %% R11B: andalso/orelse are now allowed in guards.
    {This2, L1} = {This1}:gexpr(L0),
    {This3, R1} = {This2}:gexpr(R0),			%They see the same variables
    {This3, {op,Line,Op,L1,R1}};
gexpr(This1, {op,Line,Op,L0,R0}) ->
    case erl_internal:arith_op(Op, 2) or
	  erl_internal:bool_op(Op, 2) or 
	  erl_internal:comp_op(Op, 2) of
	true ->
	    {This2, L1} = {This1}:gexpr(L0),
	    {This3, R1} = {This2}:gexpr(R0),			%They see the same variables
	    {This3, {op,Line,Op,L1,R1}}
    end.

%% -type gexpr_list([GuardExpr]) -> [GuardExpr].
%%  These expressions are processed "in parallel" for purposes of variable
%%  definition etc.

gexpr_list(This1, [E0|Es]) ->
    {This2, E1} = {This1}:gexpr(E0),
    {This3, Es1} = {This2}:gexpr_list(Es),
    {This3, [E1|Es1]};
gexpr_list(This, []) -> {This, []}.

grecord_inits(This1, [{record_field,Lf,{atom,La,F},Val0}|Is]) ->
    {This2, Val1} = {This1}:gexpr(Val0),
    {This3, Is1} = {This2}:grecord_inits(Is),
    {This3, [{record_field,Lf,{atom,La,F},Val1}|Is1]};
grecord_inits(This1, [{record_field,Lf,{var,La,'_'},Val0}|Is]) ->
    {This2, Val1} = {This1}:gexpr(Val0),
    {This3, Is1} = {This2}:grecord_inits(Is),
    {This3, [{record_field,Lf,{var,La,'_'},Val1}|Is1]};
grecord_inits(This, []) -> {This, []}.

%% -type exprs([Expression]) -> [Expression].
%%  These expressions are processed "sequentially" for purposes of variable
%%  definition etc.

exprs(This1, [E0|Es]) ->
    {This2, E1} = {This1}:expr(E0),
    {This3, Es1} = {This2}:exprs(Es),
    {This3, [E1|Es1]};
exprs(This, []) -> {This, []}.

%% -type expr(Expression) -> Expression.

expr(This, {var,Line,V}) -> {This, {var,Line,V}};
expr(This, {integer,Line,I}) -> {This, {integer,Line,I}};
expr(This, {float,Line,F}) -> {This, {float,Line,F}};
expr(This, {atom,Line,A}) -> {This, {atom,Line,A}};
expr(This, {string,Line,S}) -> {This, {string,Line,S}};
expr(This, {char,Line,C}) -> {This, {char,Line,C}};
expr(This, {nil,Line}) -> {This, {nil,Line}};
expr(This1, {cons,Line,H0,T0}) ->
    {This2, H1} = {This1}:expr(H0),
    {This3, T1} = {This2}:expr(T0),				%They see the same variables
    {This3, {cons,Line,H1,T1}};
expr(This1, {lc,Line,E0,Qs0}) ->
    {This2, Qs1} = {This1}:lc_bc_quals(Qs0),
    {This3, E1} = {This2}:expr(E0),
    {This3, {lc,Line,E1,Qs1}};
expr(This1, {bc,Line,E0,Qs0}) ->
    {This2, Qs1} = {This1}:lc_bc_quals(Qs0),
    {This3, E1} = {This2}:expr(E0),
    {This3, {bc,Line,E1,Qs1}};
expr(This1, {tuple,Line,Es0}) ->
    {This2, Es1} = {This1}:expr_list(Es0),
    {This2, {tuple,Line,Es1}};
%%expr({struct,Line,Tag,Es0}) ->
%%    Es1 = pattern_list(Es0),
%%    {struct,Line,Tag,Es1};
expr(This1, {record_index,Line,Name,Field0}) ->
    {This2, Field1} = {This1}:expr(Field0),
    {This2, {record_index,Line,Name,Field1}};
expr(This1, {record,Line,Name,Inits0}) ->
    {This2, Inits1} = {This1}:record_inits(Inits0),
    {This2, {record,Line,Name,Inits1}};
expr(This1, {record_field,Line,Rec0,Name,Field0}) ->
    {This2, Rec1} = {This1}:expr(Rec0),
    {This3, Field1} = {This2}:expr(Field0),
    {This3, {record_field,Line,Rec1,Name,Field1}};
expr(This1, {record,Line,Rec0,Name,Upds0}) ->
    {This2, Rec1} = {This1}:expr(Rec0),
    {This3, Upds1} = {This2}:record_updates(Upds0),
    {This3, {record,Line,Rec1,Name,Upds1}};
expr(This1, {record_field,Line,Rec0,Field0}) ->
    {This2, Rec1} = {This1}:expr(Rec0),
    {This3, Field1} = {This2}:expr(Field0),
    {This3, {record_field,Line,Rec1,Field1}};
expr(This1, {block,Line,Es0}) ->
    %% Unfold block into a sequence.
    {This2, Es1} = {This1}:exprs(Es0),
    {This2, {block,Line,Es1}};
expr(This1, {'if',Line,Cs0}) ->
    {This2, Cs1} = {This1}:icr_clauses(Cs0),
    {This2, {'if',Line,Cs1}};
expr(This1, {'case',Line,E0,Cs0}) ->
    {This2, E1} = {This1}:expr(E0),
    {This3, Cs1} = {This2}:icr_clauses(Cs0),
    {This3, {'case',Line,E1,Cs1}};
expr(This1, {'receive',Line,Cs0}) ->
    {This2, Cs1} = {This1}:icr_clauses(Cs0),
    {This2, {'receive',Line,Cs1}};
expr(This1, {'receive',Line,Cs0,To0,ToEs0}) ->
    {This2, To1} = {This1}:expr(To0),
    {This3, ToEs1} = {This2}:exprs(ToEs0),
    {This4 ,Cs1} = {This3}:icr_clauses(Cs0),
    {This4, {'receive',Line,Cs1,To1,ToEs1}};
expr(This1, {'try',Line,Es0,Scs0,Ccs0,As0}) ->
    {This2, Es1} = {This1}:exprs(Es0),
    {This3, Scs1} = {This2}:icr_clauses(Scs0),
    {This4, Ccs1} = {This3}:icr_clauses(Ccs0),
    {This5, As1} = {This4}:exprs(As0),
    {This5, {'try',Line,Es1,Scs1,Ccs1,As1}};
expr(This1, {'fun',Line,Body}) ->
    case Body of
	{clauses,Cs0} ->
	    {This2, Cs1} = {This1}:fun_clauses(Cs0),
	    {This2, {'fun',Line,{clauses,Cs1}}};
	{function,F,A} ->
	    {This1, {'fun',Line,{function,F,A}}};
	{function,M,F,A} ->			%R10B-6: fun M:F/A.
	    {This1, {'fun',Line,{function,M,F,A}}}
    end;
expr(This1, {call,Line,F0,As0}) ->
    %% N.B. If F an atom then call to local function or BIF, if F a
    %% remote structure (see below) then call to other module,
    %% otherwise apply to "function".
    {This2, F1} = {This1}:expr(F0),
    {This3, As1} = {This2}:expr_list(As0),
    {This3, {call,Line,F1,As1}};
expr(This1, {'catch',Line,E0}) ->
    %% No new variables added.
    {This2, E1} = {This1}:expr(E0),
    {This2, {'catch',Line,E1}};
expr(This1, {'query', Line, E0}) ->
    %% lc expression
    {This2, E} = {This1}:expr(E0),
    {This2, {'query', Line, E}};
expr(This1, {match,Line,P0,E0}) ->
    {This2, E1} = {This1}:expr(E0),
    {This3, P1} = {This2}:pattern(P0),
    {This3, {match,Line,P1,E1}};
expr(This1, {bin,Line,Fs}) ->
    {This2, Fs2} = {This1}:pattern_grp(Fs),
    {This2, {bin,Line,Fs2}};
expr(This1, {op,Line,Op,A0}) ->
    {This2, A1} = {This1}:expr(A0),
    {This2, {op,Line,Op,A1}};
expr(This1, {op,Line,Op,L0,R0}) ->
    {This2, L1} = {This1}:expr(L0),
    {This3, R1} = {This2}:expr(R0),				%They see the same variables
    {This3, {op,Line,Op,L1,R1}};
%% The following are not allowed to occur anywhere!
expr(This1, {remote,Line,M0,F0}) ->
    {This2, M1} = {This1}:expr(M0),
    {This3, F1} = {This2}:expr(F0),
    {This3, {remote,Line,M1,F1}}.

%% -type expr_list([Expression]) -> [Expression].
%%  These expressions are processed "in parallel" for purposes of variable
%%  definition etc.

expr_list(This1, [E0|Es]) ->
    {This2, E1} = {This1}:expr(E0),
    {This3, Es1} = {This2}:expr_list(Es),
    {This3, [E1|Es1]};
expr_list(This, []) -> {This, []}.

%% -type record_inits([RecordInit]) -> [RecordInit].
%%  N.B. Field names are full expressions here but only atoms are allowed
%%  by the *linter*!.

record_inits(This1, [{record_field,Lf,{atom,La,F},Val0}|Is]) ->
    {This2, Val1} = {This1}:expr(Val0),
    {This3, Is1} = {This2}:record_inits(Is),
    {This3, [{record_field,Lf,{atom,La,F},Val1}|Is1]};
record_inits(This1, [{record_field,Lf,{var,La,'_'},Val0}|Is]) ->
    {This2, Val1} = {This1}:expr(Val0),
    {This3, Is1} = {This2}:record_inits(Is),
    {This3, [{record_field,Lf,{var,La,'_'},Val1}|Is1]};
record_inits(This, []) -> {This, []}.

%% -type record_updates([RecordUpd]) -> [RecordUpd].
%%  N.B. Field names are full expressions here but only atoms are allowed
%%  by the *linter*!.

record_updates(This1, [{record_field,Lf,{atom,La,F},Val0}|Us]) ->
    {This2, Val1} = {This1}:expr(Val0),
    {This3, Us1} = {This2}:record_updates(Us),
    {This3, [{record_field,Lf,{atom,La,F},Val1}|Us1]};
record_updates(This, []) -> {This, []}.

%% -type icr_clauses([Clause]) -> [Clause].

icr_clauses(This1, [C0|Cs]) ->
    {This2, C1} = {This1}:clause(C0),
    {This3, Cs1} = {This2}:icr_clauses(Cs),
    {This3, [C1|Cs1]};
icr_clauses(This, []) -> {This, []}.

%% -type lc_bc_quals([Qualifier]) -> [Qualifier].
%%  Allow filters to be both guard tests and general expressions.

lc_bc_quals(This1, [{generate,Line,P0,E0}|Qs]) ->
    {This2, E1} = {This1}:expr(E0),
    {This3, P1} = {This2}:pattern(P0),
    {This4, Qs1} = {This3}:lc_bc_quals(Qs),
    {This4, [{generate,Line,P1,E1}|Qs1]};
lc_bc_quals(This1, [{b_generate,Line,P0,E0}|Qs]) ->
    {This2, E1} = {This1}:expr(E0),
    {This3, P1} = {This2}:pattern(P0),
    {This4, Qs1} = {This3}:lc_bc_quals(Qs),
    {This4, [{b_generate,Line,P1,E1}|Qs1]};
lc_bc_quals(This1, [E0|Qs]) ->
    {This2, E1} = {This1}:expr(E0),
    {This3, Qs1} = {This2}:lc_bc_quals(Qs),
    {This3, [E1|Qs1]};
lc_bc_quals(This, []) -> {This, []}.

%% -type fun_clauses([Clause]) -> [Clause].

fun_clauses(This1, [C0|Cs]) ->
    {This2, C1} = {This1}:clause(C0),
    {This3, Cs1} = {This2}:fun_clauses(Cs),
    {This3, [C1|Cs1]};
fun_clauses(This, []) -> {This, []}.
